#!/usr/bin/env python

MANIFEST_URL = "git@192.168.6.40:T700/manifest.git"
THEM_BRANCH = "idh_1153"
OUR_BRANCH = "eavoo_2.3.5"
IDH_SOURCE_PATH = "/home/user/cavan/8810 W12.20 P5201262614512638foreavoo.rar"
DIR_GIT_REF_HEADS = ".git/refs/heads"

import sys, os, os.path, time
from xml.dom.minidom import parse
from getopt import getopt

def pr_red_info(message):
	print "\033[31m%s\033[0m" % message

def pr_green_info(message):
	print "\033[32m%s\033[0m" % message

def pr_bold_info(message):
	print "\033[1m%s\033[0m" % message

class RepoManager:
	def __init__(self, repo_root = None):
		if not repo_root:
			self.repo_root = os.path.abspath(".")
		else:
			self.repo_root = os.path.abspath(repo_root)
		self.repo_directory = os.path.join(self.repo_root, ".repo")

	def mkdir_perents(self, pathname):
		if os.path.isdir(pathname):
			return 0

		if os.path.exists(pathname):
			os.remove(pathname)

		if self.mkdir_perents(os.path.dirname(pathname)) < 0:
			pr_red_info("%s' create perent directory failed!" % pathname)
			return -1

		os.mkdir(pathname)
		return 0

	def show_usage():
		pr_bold_info("Usage: %s [options] output" % os.path.basename(sys.argv[0]))

	def cavan_popen(self, command):
		print command
		fp = os.popen(command)
		if fp == None:
			pr_red_info("Popen `%s' failed" % command)
			return None
		lines = fp.readlines()
		fp.close()

		return lines

	def cavan_execute(self, command):
		print command
		if os.system(command) != 0:
			pr_red_info("Execute command `%s' failed" % command)
			return -1
		return 0

	def cavan_read_file(self, filename):
		try:
			fp = file(filename, 'r')
		except:
			return None

		lines = fp.readlines()
		fp.close()

		return lines

	def cavan_hard_link(self, srcpath, destpath):
		if os.path.isdir(srcpath):
			if not os.path.isdir(destpath):
				print "make directory `%s'" % destpath
				os.mkdir(destpath)
			for fn in os.listdir(srcpath):
				if self.cavan_hard_link(os.path.join(srcpath, fn), os.path.join(destpath, fn)) < 0:
					return -1
		else:
			if os.path.exists(destpath):
				os.remove(destpath)
			try:
				os.link(srcpath, destpath)
				print "Link `%s' <= `%s'" % (srcpath, destpath)
			except:
				if os.system("cp %s %s -av" % srcpath, destpath) != 0:
					return -1
		return 0

	def git_clone(self, url, branch, output, remote = None, options = ""):
		if self.mkdir_perents(output) < 0:
			pr_red_info("create directory %s failed!" % output)
			return -1

		os.chdir(output)

		if os.path.isdir(os.path.join(output, ".git")):
			if remote and self.set_remote_name(remote) < 0:
				return -1

			if self.set_remote_url(url, remote) < 0:
				return -1

			return self.cavan_execute("git reset -q && git clean -xdf && git pull && git checkout %s" % branch)

		if self.cavan_execute("rm * -rfv && git clone %s -b %s %s ." % (options, branch, url)) < 0:
			return -1

		if remote:
			return self.set_remote_name(remote)

		return 0

	def clone_manifest(self, url, branch, pathname = None):
		if not pathname:
			pathname = os.path.join(self.repo_directory, "manifest")

		if self.git_clone(url, branch, pathname) < 0:
			return -1

		manifest_path = os.path.join(self.repo_directory, "manifest.xml")
		if os.path.exists(manifest_path):
			os.remove(manifest_path)
		os.symlink("manifest/default.xml", manifest_path)

		if self.parse_xml(manifest_path) < 0:
			return -1

		return self.set_remote_name(self.remote_name, None, pathname)

	def git_checkout_file(self, output, branch, filename):
		os.chdir(output)
		for fn in self.cavan_popen("git status -b -s --porcelain | grep --color=never \"%s\"" % filename):
			if self.cavan_execute("git checkout %s %s" % (branch, fn[2:].strip())) < 0:
				return -1
		return 0

	def git_checkout_branch(self, repo_path, branch, remote = None):
		if repo_path:
			os.chdir(repo_path)

		if not remote:
			remote = self.remote_name

		if self.cavan_execute("git clean -xdf") < 0:
			return -1

		if os.path.isfile(os.path.join(DIR_GIT_REF_HEADS, branch)):
			command = "git checkout %s" % branch
		else:
			command = "git checkout %s/%s -b %s" % (remote, branch, branch)

		return self.cavan_execute(command)

	def set_remote_name(self, name, remote = None, repo_path = None):
		if repo_path:
			os.chdir(repo_path)

		if not remote:
			remote = self.cavan_popen("git remote show")[0].strip()

		if remote == name:
			return 0
		return self.cavan_execute("git remote rename %s %s" % (remote, name))

	def set_remote_url(self, url, remote = None, repo_path = None):
		if repo_path:
			os.chdir(repo_path)

		if not remote:
			remote = self.cavan_popen("git remote show")[0].strip()

		return self.cavan_execute("git config remote.%s.url %s" % (remote, url))

	def parse_xml(self, manifest_xml):
		if not manifest_xml or not os.path.exists(manifest_xml):
			manifest_xml = None
			for path in ["manifest.xml", "manifest/default.xml"]:
				abspath = os.path.join(self.repo_directory, path)
				print "abspath = " + abspath
				if os.path.exists(abspath):
					manifest_xml = abspath
					break

		if not manifest_xml:
			pr_red_info("No manifest xml found!")
			return -1

		self.manifest_dom = parse(manifest_xml)
		if not self.manifest_dom:
			return -1

		self.tag_manifet = self.manifest_dom.getElementsByTagName("manifest").item(0)
		if not self.tag_manifet:
			pr_red_info("No manifet tag found")
			return -1

		self.tag_remote = self.tag_manifet.getElementsByTagName("remote").item(0)
		if not self.tag_remote:
			pr_red_info("No remote tag found")

		self.tag_default = self.tag_manifet.getElementsByTagName("default").item(0)
		if not self.tag_default:
			pr_red_info("No default tag found")
			return -1

		self.remote_url = self.tag_remote.getAttribute("fetch")
		self.remote_name = self.tag_remote.getAttribute("name")
		self.git_branch = self.tag_default.getAttribute("revision")

		pr_bold_info("remote_url = " + self.remote_url)
		pr_bold_info("remote_name = " + self.remote_name);
		pr_bold_info("git_branch = " + self.git_branch)

		return 0

	def repo_command(self, command):
		if self.parse_xml(None) < 0:
			return -1

		for project in self.tag_manifet.getElementsByTagName("project"):
			path = project.getAttribute("path")
			name = project.getAttribute("name")
			command_last = command.replace("<path>", path).replace("<name>", name)
			abspath = os.path.join(self.repo_root, path)
			pr_bold_info("%s <= `%s'" % (path, command_last))
			if self.mkdir_perents(abspath) < 0:
				pr_red_info("create directory %s failed!" % abspath)
				return -1

			os.chdir(abspath)
			if self.cavan_execute(command_last) < 0:
				return -1
		return 0

	def repo_init(self, manifest_url, manifest_branch):
		if self.clone_manifest(manifest_url, manifest_branch) < 0:
			pr_red_info("clone manifest failed!")
			return -1

		if self.parse_xml(None) < 0:
			pr_red_info("patse manifest xml failed!")
			return -1

		for project in self.tag_manifet.getElementsByTagName("project"):
			path = project.getAttribute("path")
			name = project.getAttribute("name")
			url = os.path.join(self.remote_url, name) + ".git"
			output = os.path.join(self.repo_root, path)
			pr_bold_info("clone `%s'" % url)

			if os.path.isdir(output) and self.cavan_execute("rm %s/* -rfv" % output) < 0:
				return -1

			if self.git_clone(url, self.git_branch, output, self.remote_name, "-n") < 0:
				pr_red_info("git clone %s failed!" % name)
				return -1

			if self.cavan_execute("rm %s/* -rfv" % output) < 0:
				return -1

			if self.git_checkout_file(output, self.git_branch, ".gitignore") < 0:
				return -1
		return 0

	def decom_idh_package(self, idh_path, output):
		if self.cavan_execute("rm %s -rfv" % output) < 0:
			return -1

		if self.mkdir_perents(output) != 0:
			pr_red_info("create directory %s failed" % output)
			return -1

		os.chdir(output)

		if self.cavan_execute("rar x \"%s\"" % idh_path) < 0:
			return -1

		for fn in self.cavan_popen("find -name *.tgz"):
			if self.cavan_execute("tar -xvf \"%s\"" % fn.strip()) < 0:
				return -1

		for fn in ["csr", "inno"]:
			if not os.path.isdir(fn):
				continue

			if self.cavan_execute("cp %s/* idh.code -av" % fn) < 0:
				return -1
		return 0

	def repo_merge(self, manifest_url, them_code, our_branch, them_branch):
		pr_bold_info("manifest_url = " + manifest_url)
		pr_bold_info("them_branch = " + them_branch)
		pr_bold_info("our_branch = " + our_branch)
		pr_bold_info("them_code = " + them_code)

		if self.repo_init(manifest_url, them_branch) < 0:
			pr_red_info("init repo failed!")
			return -1

		if not os.path.isdir(them_code):
			tmp_idh_path = os.path.join(self.repo_root, "idh")
			if self.decom_idh_package(them_code, tmp_idh_path) < 0:
				return -1
			them_code = os.path.join(tmp_idh_path, "idh.code")

		if self.cavan_hard_link(them_code, self.repo_root) < 0:
			return -1

		date = time.localtime(time.time())
		date_message = "%d.%02d.%d %d:%02d:%02d" % (date[0], date[1], date[2], date[3], date[4], date[5])
		message_them = "Commit newest code when %s" % date_message
		message_our = "Merge with branch '%s' when %s" % (them_branch, date_message)

		for project in self.tag_manifet.getElementsByTagName("project"):
			path = project.getAttribute("path")
			abspath = os.path.join(self.repo_root, path)
			os.chdir(abspath)
			self.cavan_execute("git add . && git commit -asm \"%s\"" % message_them)
			if self.git_checkout_branch(abspath, our_branch, None) != 0:
				pr_red_info("merge code failed!")
				return -1

			if self.cavan_execute("git merge %s" % them_branch) == 0:
				continue

			while True:
				os.system("git status")
				pr_bold_info("Project path: %s" % abspath)
				raw_input("Please merge manual and press ENTER continue")
				if self.cavan_execute("git commit -sm \"%s\"" % message_our) == 0:
					break

		pr_green_info("Repo merge is OK")
		return 0

if __name__ == "__main__":
	if len(sys.argv) < 2:
		pr_red_info("Please give some arguments")
		sys.exit(-1)

	subcmd = sys.argv[1]
	argv = sys.argv[2:]

	if subcmd == "command":
		repo = RepoManager(None)
		status = repo.repo_command(" ".join(argv))
	elif subcmd == "merge":
		if len(argv) > 0:
			repo = RepoManager(argv[0])
		else:
			repo = RepoManager(None)
		status = repo.repo_merge(MANIFEST_URL, IDH_SOURCE_PATH, OUR_BRANCH, THEM_BRANCH)
	else:
		pr_red_info("Unknown subcmd `%s'" % subcmd)
		status = -1

	sys.exit(status)
